package io.gitee.zhucan123.core.infrastructure.util;

import com.google.common.collect.Lists;
import org.apache.commons.collections4.IteratorUtils;
import org.apache.commons.lang3.NotImplementedException;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * @author guodu
 * @date 2019/10/21
 */
public class CollectionUtils {

  /**
   * 从 collection 中移除并返回第一个满足条件的元素.
   */
  public static <T> T peek(Iterable<T> collection, Predicate<T> predicate) {
    Iterator<T> it = collection.iterator();
    while (it.hasNext()) {
      T ele = it.next();
      if (predicate.test(ele)) {
        it.remove();
        return ele;
      }
    }
    return null;
  }

  /**
   * Foreach的时候，提供序号: list.forEach(i, ele -> {})
   *
   * @param collection
   * @param consumer
   * @param <T>
   */
  public static <T> void forEach(Collection<T> collection, BiConsumer<Integer, T> consumer) {
    Iterator<T> it = collection.iterator();
    int i = 0;
    while (it.hasNext()) {
      consumer.accept(i, it.next());
      i++;
    }
  }

  public static <T> List<List<T>> combine(Collection<T> list, boolean noDuplicate) {
    int len = list.size();
    List<List<T>> ret = new ArrayList<>();
    // 初始化
    for (T t : list) {
      ret.add(Lists.newArrayList(t));
    }
    int[] indexes = new int[len];
    indexes[0] = ret.size();

    for (int i = 1; i < len; i++) {
      // loop: 取上一次循环生成的元素，再添加新的元素进行组合
      // size个元素的排列组合
      int size = i + 1;
      // it: 指向(i - 1)的时候生成的组合的起始位置
      Iterator<List<T>> it = ret.iterator();
      int lastLoopStart;
      if(i - 2 > -1) {
        lastLoopStart = indexes[i - 2];
        it = IteratorUtils.skippingIterator(it, lastLoopStart);
      }
      List<List<T>> newCombines= new ArrayList<>();
      while (it.hasNext()) {
        List<T> ele = it.next();
        // 将list中的元素逐个组合到ele中
        Iterator<T> conIt = list.iterator();
        if(noDuplicate) {
          // 如果要去重复，则取ele中的最后一个元素，从那个元素开始新的组合
          T eleTail = ele.get(ele.size() - 1);
          while(conIt.hasNext()) {
            if(conIt.next() == eleTail) {
              break;
            }
          }
        }
        // 从元素中取出一个进行组合
        while(conIt.hasNext()) {
          List<T> newCombine = new ArrayList<>(ele.size() + 1);
          newCombine.addAll(ele);
          newCombine.add(conIt.next());
          newCombines.add(newCombine);
        }
      }
      ret.addAll(newCombines);
    }
    return ret;
  }

  /**
   * 排列组合元素。如果不需要保存全部结果则使用这个方法，能节省内存。
   * 组合结果没有去重，调用者处理
   * @param minEle 组合结果最小元素数量
   * @param maxEle 组合结果中取的最多元素数量
   * @param onEle 处理组合结果
   */
  public static <T> void combine(List<T> list, int minEle, int maxEle, Predicate<List<T>> onEle) {
    if (minEle < 1 || maxEle > list.size()) {
      throw new IllegalArgumentException();
    }
    int scale = list.size();
    for (int combineLen = minEle; combineLen <= maxEle; combineLen++) {
      // 组合结果list每一个元素从list中选一个，因此，用一个进制为list.size的数表示排列组合结果里面各index
      long counter = 0;
      // counter 的 combineLen 进制表示形式
      while (true) {
        List<Integer> bits = NumberUtils.scale(counter, scale);
        if (bits.size() > combineLen) {
          break;
        }
        counter++;
        // padding left
        while (bits.size() < combineLen) {
          bits.add(0, 0);
        }
        List<T> result = new ArrayList<>(combineLen);
        for (Integer bit : bits) {
          result.add(list.get(bit));
        }
        Boolean stop = onEle.test(result);
        if (stop != null && stop) {
          break;
        }
      }
    }
  }

  /**
   * @param comparator 比较实体的前后状态，返回null表示不是同一个实体，true表示相同没有更改，false表示有更改
   */
  public static <T, R> void diff(
      Collection<T> curr, Collection<R> prev, BiFunction<T, R, Boolean> comparator,
      Collection<T> created, Collection<R> deleted, Map<R, T> updated) {
    // 假定 prev 所有的都删除了，再从deleted中找到修改的，将修改的从deleted移除
    deleted.addAll(prev);
    created.addAll(curr);
    Iterator<R> prevIt = deleted.iterator();
    while (prevIt.hasNext()) {
      R p = prevIt.next();
      Iterator<T> currIt = created.iterator();
      while (currIt.hasNext()) {
        T c = currIt.next();
        Boolean b = comparator.apply(c, p);
        if (b == null) {
          // 不是同一个实体，一定要 continue
          continue;
        }
        if (b) {
          // 没有更改
        } else {
          updated.put(p, c);
        }
        prevIt.remove();
        currIt.remove();
        break;
      }
      if (!currIt.hasNext()) {
        // curr 中找不到 p 了，确实是删除了，把p留在deleted中
      }
    }
  }

  public static <E, R> int retainCount(Collection<E> collection, Collection<R> retain, BiFunction<E, R, Boolean> comparator) {
    int count = 0;
    for (final E obj : collection) {
      for (R r : retain) {
        Boolean match = comparator.apply(obj, r);
        if(match != null && match) {
          count++;
          break;
        }
      }
    }
    return count;
  }

  public interface DTOAssembler<ENTITY, DTO> extends Function<ENTITY, DTO> {

    void appendChild(DTO parent, DTO child);

    boolean isChild(DTO parent, DTO child);

    /**
     * Optional，不想继承这个方法则抛出NotImplementedException
     */
    int compare(DTO o1, DTO o2);

    boolean isRoot(DTO dto);

  }

  public static <DTO, ENTITY> Collection<DTO> convertToTree(
      Collection<ENTITY> list, DTOAssembler<ENTITY, DTO> assembler) {
    boolean optimize;
    try {
      assembler.compare(null, null);
      optimize = true;
    } catch (NotImplementedException e) {
      optimize = false;
    }
    Collection<DTO> tree;
    tree = optimize ? new TreeSet<>(assembler::compare) : new HashSet<>();
    for (ENTITY ele : list) {
      DTO dto = assembler.apply(ele);
      tree.add(dto);
    }
    for (DTO dto : tree) {
      DTO parent = IteratorUtils.find(tree.iterator(), p -> assembler.isChild(p, dto));
      if (parent != null) {
        assembler.appendChild(parent, dto);
      }
    }
    tree.removeIf(ele -> !assembler.isRoot(ele));
    return tree;
  }

  public static <T> void move(List<T> list, T moving, T target, boolean before) {
    if (moving == null || target == null || moving.equals(target)) {
      throw new IllegalArgumentException();
    }
    ListIterator<T> it = list.listIterator();
    while (it.hasNext()) {
      T ele = it.next();
      if (ele.equals(moving)) {
        it.remove();
      } else if (ele.equals(target)) {
        if (before) {
          it.previous();
          it.add(moving);
          it.next();
        } else {
          it.add(moving);
        }
      }
    }
  }

}
